<template>
  <view class="tv-view" :class="[hasFocus ? 'focus' : '']">
    <slot></slot>
  </view>
</template>

<script>
/**
 * tv-view 视图容器
 * @description 类似于view，用于包裹获取焦点的元素，需要在tv-page内使用
 * @property { defaultFocus } 默认焦点
 */
import { getBestCandidateRect } from '../../util/focus.js';
import mixin from '../../mixin/mixin.js';

export default {
  name: 'TvView',
  componentName: 'TvView',
  mixins: [mixin],
  inject: ['tvPage', 'tvArea'],
  props: {
    // 默认焦点
    defaultFocus: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    // 是否获得焦点
    hasFocus() {
      return this.focusFlag;
    },
    // 能够获得焦点
    canTakeFocus() {
      return !this.hasFocus && !!this.rect.height && !!this.rect.width;
    }
  },
  data: () => ({
    focusFlag: false, // 焦点标志
    rect: {} // 节点信息
  }),
  watch: {
    defaultFocus: {
      immediate: true,
      handler(val) {
        val && this.setFocusView();
      }
    }
  },
  methods: {
    /**
    * 派遣按键事件
    * @param {String} keyType 按键类型
    * @param {Number} keyCode 按键编码
    */
    dispatchKeyEvent(keyType, keyCode) {
      // 已获得焦点
      if (this.hasFocus) {
        // 按键事件处理
        this.handleKeyEvent(keyType);
      }
    },
    /**
    * 处理按键事件
    * @param {String} keyType 按键类型
    */
    handleKeyEvent(keyType) {
      // 阻止默认事件
      // 处理按键事件
      ['up', 'down', 'left', 'right'].includes(keyType) && this.findNextFocusView(keyType);
      ['enter'].includes(keyType) && this.$emit('tvClick');
    },
    /**
    * 查找下一个焦点
    * @param {String} keyType 按键类型
    */
    findNextFocusView(keyType) {
      const tvViews = this.tvArea.children.length && this.tvArea.children.filter(v => v.$options.componentName === 'TvView');
      const betterCandidate = getBestCandidateRect(this, tvViews, keyType);
      betterCandidate && this.changeFocusView(betterCandidate);
    },
    /**
    * 设置焦点对象
    */
    setFocusView() {
      if (this.canTakeFocus || this.defaultFocus) {
        this.focusFlag = true;
        this.tvArea.focused = this;
      }
    },
    /**
    * 改变焦点对象
    * @param {Object} betterCandidate 最佳焦点元素
    */
    changeFocusView(betterCandidate) {
      this.focusFlag = false;
      betterCandidate.setFocusView();
    }
  },
  async mounted() {
    const rect = await this.$getRect('.tv-view');
    this.rect = Object.assign({}, rect, { uid: this._uid });
    this.tvArea.children.push(this);
  }
};
</script>

<style lang="scss" scoped>
.tv-view {
  box-sizing: border-box;
  &.focus {
    border: 1rpx solid #1FA2FF;
  }
}
</style>
